#include <iostream>			// Header File For Streams; Used By LoadMap
#include <fstream>			// Header File For File Streams; Used by loadmap
#include <math.h>			// Header File For Math Functions; used for fmod
#include "ProjectHeader.h"
#include "CatalogueLoader.h"
#include "SceneMan.h"		// Header File For Class Decleration

SceneMan::SceneMan()
{
	xSize = 0;
	ySize = 0;
	GridSpace = 1.0f;

	CatalogueLoader::LoadScene("Default.cat", CubeMap, Cubes, GridSpace);
	xSize = CubeMap.size();
	if(xSize){ySize = CubeMap[0].size();}
}

bool SceneMan::ContainsBox(vertex2f btmLft, vertex2f topRgt)
{
	GLuint xStart = btmLft.u < topRgt.u ? (GLuint)(btmLft.u / GridSpace) : (GLuint)(topRgt.u / GridSpace);
	GLuint yStart = btmLft.v < topRgt.v ? (GLuint)(btmLft.v / GridSpace) : (GLuint)(topRgt.v / GridSpace);
	GLuint xEnd = btmLft.u > topRgt.u	? (GLuint)(btmLft.u / GridSpace) : (GLuint)(topRgt.u / GridSpace);
	GLuint yEnd = btmLft.v > topRgt.v	? (GLuint)(btmLft.v / GridSpace) : (GLuint)(topRgt.v / GridSpace);
	//This ensures that xStart and yStart are both smaller than xEnd and yEnd.

	for(GLuint x = xStart; x < xEnd; x++)
	{
		for(GLuint y = yStart; y < yEnd; y++)
		{
			if(!Cubes[CubeMap[x][y]].CantTouchThis){return false;}
			//Return false if any cube within the area enclosed by the box is touchable.
			//No need to worry that CubeMap[x][y] doesn't exist; this is checked for by
			//the CatalogueLoader.
		}
	}

	return true;
}

bool SceneMan::ContainsPoint(vertex2f point)
{
	GLuint xIndex = (GLuint)(point.u / GridSpace);
	GLuint yIndex = (GLuint)(point.v / GridSpace);

	return !(Cubes[CubeMap[xIndex][yIndex]].CantTouchThis);	
	//If no touchable cubes at the point, return false
	//No need to worry that CubeMap[x][y] doesn't exist; this is checked for by
	//the CatalogueLoader.
}
bool SceneMan::Draw(std::vector<vertex> * VboVec, const std::map<int, texCoord2f>& TexMap, GLuint TexAtlas)
{	
	int WorstCaseSize = (3 * xSize * ySize + 6) * 64;
	//Worst Case of 3*area +6 faces, 64 floats per face
	VboVec->reserve(WorstCaseSize);				//Just in case
	GLuint x = 0; GLuint y = 0; GLuint Tex = 0;	//starts at cube 0,0.
	vertex3f cubeVerts[8];	//Holds all the vertices used by a cube
	vertex3f Normals[6];	//Holds the normals for each face
	vertex v1; vertex v2; vertex v3; vertex v4;

		Normals[FRONT].x =   0.0f;	Normals[FRONT].y =   0.0f;	Normals[FRONT].z =   1.0f;	//Front Normal
		Normals[TOP].x =	 0.0f;	Normals[TOP].y =	 1.0f;	Normals[TOP].z =	 0.0f;	//Top Normal
		Normals[RIGHT].x =   1.0f;	Normals[RIGHT].y =   0.0f;	Normals[RIGHT].z = 0.0f;	//Right Normal
		Normals[BOTTOM].x =  0.0f;	Normals[BOTTOM].y = -1.0f;	Normals[BOTTOM].z =	 0.0f;	//Bottom Normal
		Normals[LEFT].x =	-1.0f;	Normals[LEFT].y =	 0.0f;	Normals[LEFT].z =    0.0f;	//Left Normal
		Normals[BACK].x =	 0.0f;	Normals[BACK].y =    0.0f;	Normals[BACK].z =	-1.0f;	//Back Normal
	//Normals are always the same.
		
		cubeVerts[4].z = -GridSpace;
		cubeVerts[2].y = GridSpace;
		cubeVerts[7].y = GridSpace;
		cubeVerts[7].z = -GridSpace;
		//This is to prevent the 1st cube from being screwed up.

	//BUG Make sure xSize and ySize are accurate
	for(GLuint cell = 0; cell < xSize * ySize; cell++)		
		//This gets looped per cube
	{

		cubeVerts[5] = cubeVerts[4];
		cubeVerts[0] = cubeVerts[1];
		cubeVerts[3] = cubeVerts[2];
		cubeVerts[6] = cubeVerts[7];
		//Reuse last cube's Rgt verts as this cube's Lft verts

		cubeVerts[1].x = x * GridSpace + GridSpace;	
		cubeVerts[1].y = y * GridSpace;				
		cubeVerts[1].z = 0;

		cubeVerts[2].x = x * GridSpace + GridSpace;	
		cubeVerts[2].y = y * GridSpace + GridSpace;	
		cubeVerts[2].z = 0;

		cubeVerts[4].x = cubeVerts[1].x;			
		cubeVerts[4].y = cubeVerts[1].y;			
		cubeVerts[4].z = -GridSpace;

		cubeVerts[7].x = cubeVerts[2].x;			
		cubeVerts[7].y = cubeVerts[2].y;			
		cubeVerts[7].z = -GridSpace;

		//This colossal lump of code basically fills an array with the 
		//8 verts that the cube is comprised of.
		//It's set out as show below by this marvelous diagram
		//     6----7
		//    /|   /|
		//   3----2 |
		//   | 5--|-4
		//	 |/   |/
		//   0----1

		for(int i = 0; i < 6; i++)	//this gets looped per-face
		{
			//BUG Make sure that the  cubes exist first!
			if(DrawableFace(x,y,i))
			{
				switch(i) {
				case FRONT:
					v1.position = cubeVerts[0]; v2.position = cubeVerts[1];
					v3.position = cubeVerts[2]; v4.position = cubeVerts[3];
					v1.normal = Normals[FRONT];	v2.normal = Normals[FRONT];
					v3.normal = Normals[FRONT];	v4.normal = Normals[FRONT];
					break;										//Front
				case TOP:
					v1.position = cubeVerts[3]; v2.position = cubeVerts[2];
					v3.position = cubeVerts[7]; v4.position = cubeVerts[6];
					v1.normal =  Normals[TOP];	v2.normal =   Normals[TOP];
					v3.normal =  Normals[TOP];	v4.normal =   Normals[TOP];
					break;										//Top
				case RIGHT:
					v1.position = cubeVerts[1]; v2.position = cubeVerts[4];
					v3.position = cubeVerts[7]; v4.position = cubeVerts[2];
					v1.normal = Normals[RIGHT];	v2.normal = Normals[RIGHT];
					v3.normal = Normals[RIGHT];	v4.normal = Normals[RIGHT];
					break;										//Right
				case BOTTOM:
					v1.position = cubeVerts[5]; v2.position = cubeVerts[4];
					v3.position = cubeVerts[1]; v4.position = cubeVerts[0];
					v1.normal =Normals[BOTTOM];	v2.normal =Normals[BOTTOM];
					v3.normal =Normals[BOTTOM];	v4.normal =Normals[BOTTOM];
					break;										//Bottom
				case LEFT:
					v1.position = cubeVerts[5]; v2.position = cubeVerts[0];
					v3.position = cubeVerts[3]; v4.position = cubeVerts[6];
					v1.normal =  Normals[LEFT];	v2.normal =  Normals[LEFT];
					v3.normal =  Normals[LEFT];	v4.normal =  Normals[LEFT];
					break;										//Left
				case BACK:
					v1.position = cubeVerts[4]; v2.position = cubeVerts[5];
					v3.position = cubeVerts[6]; v4.position = cubeVerts[7];
					v1.normal =  Normals[BACK];	v2.normal =  Normals[BACK];
					v3.normal =  Normals[BACK];	v4.normal =  Normals[BACK];
					break;										//Back
				}

				//BUG Make sure that the textures exist first
				Tex = Cubes[CubeMap[x][y]].Texture[i];
				//grab the TexId of this face
				if(TexMap.find(Tex) == TexMap.end()){continue;}
				//HACK There should be a central method to check that ALL of the textures referenced exist.
				if(TexMap.find(Tex)->second.TexIndex != TexAtlas){continue;}
				//skip this face if the texture is in another texture atlas
				v1.texCoord = TexMap.find(Tex)->second.btmLft;
				v2.texCoord = TexMap.find(Tex)->second.btmRgt;
				v3.texCoord = TexMap.find(Tex)->second.topRgt;
				v4.texCoord = TexMap.find(Tex)->second.topLft;

				//First Triangle
				VboVec->push_back(v1);
				VboVec->push_back(v2);
				VboVec->push_back(v3);
		
				//Second Triangle
				VboVec->push_back(v1);
				VboVec->push_back(v3);
				VboVec->push_back(v4);
			}
		}
		++x; 
		if(x == xSize){
			x = 0; ++y;
			cubeVerts[1].clear(); cubeVerts[2].clear();
			cubeVerts[7].clear(); cubeVerts[4].clear();
			
			cubeVerts[1].x = 0;
			cubeVerts[1].y = y * GridSpace;
			cubeVerts[1].z = 0;

			cubeVerts[2].x = 0;
			cubeVerts[2].y = (y + 1) * GridSpace;
			cubeVerts[2].z = 0;

			cubeVerts[7].x = 0;
			cubeVerts[7].y = (y + 1) * GridSpace;
			cubeVerts[7].z = -GridSpace;

			cubeVerts[4].x = 0;
			cubeVerts[4].y = y * GridSpace;
			cubeVerts[4].z = -GridSpace;

			//clears verts 1, 2, 7 and 4. This is because these verts get
			//reused for the next cube; the current values would be invalid.
		}	
		//True if we reach the end of the row. Sets next cell to the first
		//element of the next row.
		
	}
	return true;
}
bool SceneMan::DrawableFace(GLuint x, GLuint y, int i)
	{
		if(!Cubes[CubeMap[x][y]].Drawable){return false;}
		//Front and Back faces always get drawn, so return true if it's a front face
		if(i == FRONT || i == BACK) {return true;}

		//Second, check if it's on an edge, 
		//so we don't try to access invalid indices
		if(
			(i == TOP		&& y == (ySize -1)) ||	//Top face, top row
			(i == RIGHT		&& x == (xSize -1)) ||	//Right face, rightmost row
			(i == BOTTOM	&& y == 0) ||			//Bottom face, bottom row 
			(i == LEFT		&& x == 0)				//Left face, leftmost row
		){return true;}

		//Lastly, return true if the adjacent cube isn't drawable
		if(
			(i == TOP		&& !Cubes[CubeMap[x][y+1]].Drawable) ||
			(i == RIGHT		&& !Cubes[CubeMap[x+1][y]].Drawable) ||
			(i == BOTTOM	&& !Cubes[CubeMap[x][y-1]].Drawable) ||
			(i == LEFT		&& !Cubes[CubeMap[x-1][y]].Drawable)
		){return true;}

		//If it's not returned true by now, then it's not drawable
		return false;
	}